/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Sleepycat Software. All rights reserved. * * $Id: MemoryBudget.java,v 1.1 2006/05/06 09:00:26 ckaestne Exp $ */ package com.sleepycat.je.dbi; import java.util.Iterator; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.EnvironmentStats; import com.sleepycat.je.StatsConfig; import com.sleepycat.je.config.EnvironmentParams; import com.sleepycat.je.latch.LatchSupport; import com.sleepycat.je.tree.BIN; import com.sleepycat.je.tree.DBIN; import com.sleepycat.je.tree.DIN; import com.sleepycat.je.tree.IN; /** * MemoryBudget calculates the available memory for JE and how to apportion * it between cache and log buffers. It is meant to centralize all memory * calculations. Objects that ask for memory budgets should get settings from * this class, rather than using the configuration parameter values directly. */ public class MemoryBudget implements EnvConfigObserver { /* * Object overheads. These are set statically with advance measurements. * Java doesn't provide a way of assessing object size dynamically. These * overheads will not be precise, but are close enough to let the system * behave predictably. * * _32 values are the same on Windows and Solaris. * _64 values are from 1.5.0_05 on Solaris. * _14 values are from 1.4.2 on Windows and Solaris. * _15 values are from 1.5.0_05 on Solaris and Windows. * * Specifically: * * java.vm.version=1.5.0_05_b05 os.name=SunOS * java.vm.version=1.4.2_05_b04 os.name=SunOS * java.vm.version=1.5.0_04_b05, os.name=Windows XP * java.vm.version=1.4.2_06-b03, os.name=Windows XP * * The integer following the // below is the Sizeof argument used to * compute the value. */ // 7 private final static int LONG_OVERHEAD_32 = 16; private final static int LONG_OVERHEAD_64 = 24; // 8 - 2560 private final static int BYTE_ARRAY_OVERHEAD_32 = 16; private final static int BYTE_ARRAY_OVERHEAD_64 = 24; // 2 private final static int OBJECT_OVERHEAD_32 = 8; private final static int OBJECT_OVERHEAD_64 = 16; // (4 - BYTE_ARRAY_OVERHEAD_32) / 256 private final static int ARRAY_ITEM_OVERHEAD_32 = 4; private final static int ARRAY_ITEM_OVERHEAD_64 = 8; // 20 private final static int HASHMAP_OVERHEAD_32 = 120; private final static int HASHMAP_OVERHEAD_64 = 216; // 21 - OBJECT_OVERHEAD_32 - HASHMAP_OVERHEAD_32 private final static int HASHMAP_ENTRY_OVERHEAD_32 = 24; private final static int HASHMAP_ENTRY_OVERHEAD_64 = 48; // 22 private final static int HASHSET_OVERHEAD_32 = 136; private final static int HASHSET_OVERHEAD_64 = 240; // 23 - OBJECT_OVERHEAD_32 - HASHSET_OVERHEAD_32 private final static int HASHSET_ENTRY_OVERHEAD_32 = 24; private final static int HASHSET_ENTRY_OVERHEAD_64 = 48; // 2 * HASHMAP_OVERHEAD_32 private final static int TWOHASHMAPS_OVERHEAD_32 = 240; private final static int TWOHASHMAPS_OVERHEAD_64 = 432; // 34 private final static int TREEMAP_OVERHEAD_32 = 40; private final static int TREEMAP_OVERHEAD_64 = 64; // 35 - OBJECT_OVERHEAD_32 - TREEMAP_OVERHEAD_32 private final static int TREEMAP_ENTRY_OVERHEAD_32 = 32; private final static int TREEMAP_ENTRY_OVERHEAD_64 = 53; // 9 private final static int LN_OVERHEAD_32 = 24; private final static int LN_OVERHEAD_64 = 32; // 19 private final static int DUPCOUNTLN_OVERHEAD_32 = 24; private final static int DUPCOUNTLN_OVERHEAD_64 = 40; // 12 private final static int BIN_FIXED_OVERHEAD_32_14 = 344; private final static int BIN_FIXED_OVERHEAD_32_15 = 360; private final static int BIN_FIXED_OVERHEAD_64_15 = 528; // 18 private final static int DIN_FIXED_OVERHEAD_32_14 = 352; private final static int DIN_FIXED_OVERHEAD_32_15 = 360; private final static int DIN_FIXED_OVERHEAD_64_15 = 536; // 17 private final static int DBIN_FIXED_OVERHEAD_32_14 = 352; private final static int DBIN_FIXED_OVERHEAD_32_15 = 368; private final static int DBIN_FIXED_OVERHEAD_64_15 = 544; // 13 private final static int IN_FIXED_OVERHEAD_32_14 = 312; private final static int IN_FIXED_OVERHEAD_32_15 = 320; private final static int IN_FIXED_OVERHEAD_64_15 = 472; // 6 private final static int KEY_OVERHEAD_32 = 16; private final static int KEY_OVERHEAD_64 = 24; // 24 private final static int LOCK_OVERHEAD_32 = 32; private final static int LOCK_OVERHEAD_64 = 56; // 25 private final static int LOCKINFO_OVERHEAD_32 = 16; private final static int LOCKINFO_OVERHEAD_64 = 32; /* * Txn memory is the size for the Txn + a hashmap entry * overhead for being part of the transaction table. */ // 15 private final static int TXN_OVERHEAD_32_14 = 167; private final static int TXN_OVERHEAD_32_15 = 175; private final static int TXN_OVERHEAD_64_15 = 293; // 26 private final static int CHECKPOINT_REFERENCE_SIZE_32_14 = 32 + HASHSET_ENTRY_OVERHEAD_32; private final static int CHECKPOINT_REFERENCE_SIZE_32_15 = 40 + HASHSET_ENTRY_OVERHEAD_32; private final static int CHECKPOINT_REFERENCE_SIZE_64_15 = 56 + HASHSET_ENTRY_OVERHEAD_64; /* The per-log-file bytes used in UtilizationProfile. */ // 29 / 500 private final static int UTILIZATION_PROFILE_ENTRY_32 = 88; private final static int UTILIZATION_PROFILE_ENTRY_64 = 136; /* Tracked File Summary overheads. */ // 31 private final static int TFS_LIST_INITIAL_OVERHEAD_32 = 464; private final static int TFS_LIST_INITIAL_OVERHEAD_64 = 504; // 30 private final static int TFS_LIST_SEGMENT_OVERHEAD_32 = 440; private final static int TFS_LIST_SEGMENT_OVERHEAD_64 = 464; // 33 private final static int LN_INFO_OVERHEAD_32 = 24; private final static int LN_INFO_OVERHEAD_64 = 48; /* Approximate element size in an ArrayList of Long. */ // (28 - 27) / 100 private final static int LONG_LIST_PER_ITEM_OVERHEAD_32 = 20; private final static int LONG_LIST_PER_ITEM_OVERHEAD_64 = 32; public final static int LONG_OVERHEAD; public final static int BYTE_ARRAY_OVERHEAD; public final static int OBJECT_OVERHEAD; public final static int ARRAY_ITEM_OVERHEAD; public final static int HASHMAP_OVERHEAD; public final static int HASHMAP_ENTRY_OVERHEAD; public final static int HASHSET_OVERHEAD; public final static int HASHSET_ENTRY_OVERHEAD; public final static int TWOHASHMAPS_OVERHEAD; public final static int TREEMAP_OVERHEAD; public final static int TREEMAP_ENTRY_OVERHEAD; public final static int LN_OVERHEAD; public final static int DUPCOUNTLN_OVERHEAD; public final static int BIN_FIXED_OVERHEAD; public final static int DIN_FIXED_OVERHEAD; public final static int DBIN_FIXED_OVERHEAD; public final static int IN_FIXED_OVERHEAD; public final static int KEY_OVERHEAD; public final static int LOCK_OVERHEAD; public final static int LOCKINFO_OVERHEAD; public final static int TXN_OVERHEAD; public final static int CHECKPOINT_REFERENCE_SIZE; public final static int UTILIZATION_PROFILE_ENTRY; public final static int TFS_LIST_INITIAL_OVERHEAD; public final static int TFS_LIST_SEGMENT_OVERHEAD; public final static int LN_INFO_OVERHEAD; public final static int LONG_LIST_PER_ITEM_OVERHEAD; private final static String JVM_ARCH_PROPERTY = "sun.arch.data.model"; private final static String FORCE_JVM_ARCH = "je.forceJVMArch"; static { boolean is64 = false; boolean isJVM14 = (LatchSupport.getJava5LatchClass() == null);//TODO default value necessary String overrideArch = System.getProperty(FORCE_JVM_ARCH); try { if (overrideArch == null) { String arch = System.getProperty(JVM_ARCH_PROPERTY); if (arch != null) { is64 = Integer.parseInt(arch) == 64; } } else { is64 = Integer.parseInt(overrideArch) == 64; } } catch (NumberFormatException NFE) { NFE.printStackTrace(System.err); } if (is64) { if (isJVM14) { RuntimeException RE = new RuntimeException ("1.4 based 64 bit JVM not supported"); RE.printStackTrace(System.err); throw RE; } LONG_OVERHEAD = LONG_OVERHEAD_64; BYTE_ARRAY_OVERHEAD = BYTE_ARRAY_OVERHEAD_64; OBJECT_OVERHEAD = OBJECT_OVERHEAD_64; ARRAY_ITEM_OVERHEAD = ARRAY_ITEM_OVERHEAD_64; HASHMAP_OVERHEAD = HASHMAP_OVERHEAD_64; HASHMAP_ENTRY_OVERHEAD = HASHMAP_ENTRY_OVERHEAD_64; HASHSET_OVERHEAD = HASHSET_OVERHEAD_64; HASHSET_ENTRY_OVERHEAD = HASHSET_ENTRY_OVERHEAD_64; TWOHASHMAPS_OVERHEAD = TWOHASHMAPS_OVERHEAD_64; TREEMAP_OVERHEAD = TREEMAP_OVERHEAD_64; TREEMAP_ENTRY_OVERHEAD = TREEMAP_ENTRY_OVERHEAD_64; LN_OVERHEAD = LN_OVERHEAD_64; DUPCOUNTLN_OVERHEAD = DUPCOUNTLN_OVERHEAD_64; BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_64_15; DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_64_15; DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_64_15; IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_64_15; TXN_OVERHEAD = TXN_OVERHEAD_64_15; CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_64_15; KEY_OVERHEAD = KEY_OVERHEAD_64; LOCK_OVERHEAD = LOCK_OVERHEAD_64; LOCKINFO_OVERHEAD = LOCKINFO_OVERHEAD_64; UTILIZATION_PROFILE_ENTRY = UTILIZATION_PROFILE_ENTRY_64; TFS_LIST_INITIAL_OVERHEAD = TFS_LIST_INITIAL_OVERHEAD_64; TFS_LIST_SEGMENT_OVERHEAD = TFS_LIST_SEGMENT_OVERHEAD_64; LN_INFO_OVERHEAD = LN_INFO_OVERHEAD_64; LONG_LIST_PER_ITEM_OVERHEAD = LONG_LIST_PER_ITEM_OVERHEAD_64; } else { LONG_OVERHEAD = LONG_OVERHEAD_32; BYTE_ARRAY_OVERHEAD = BYTE_ARRAY_OVERHEAD_32; OBJECT_OVERHEAD = OBJECT_OVERHEAD_32; ARRAY_ITEM_OVERHEAD = ARRAY_ITEM_OVERHEAD_32; HASHMAP_OVERHEAD = HASHMAP_OVERHEAD_32; HASHMAP_ENTRY_OVERHEAD = HASHMAP_ENTRY_OVERHEAD_32; HASHSET_OVERHEAD = HASHSET_OVERHEAD_32; HASHSET_ENTRY_OVERHEAD = HASHSET_ENTRY_OVERHEAD_32; TWOHASHMAPS_OVERHEAD = TWOHASHMAPS_OVERHEAD_32; TREEMAP_OVERHEAD = TREEMAP_OVERHEAD_32; TREEMAP_ENTRY_OVERHEAD = TREEMAP_ENTRY_OVERHEAD_32; LN_OVERHEAD = LN_OVERHEAD_32; DUPCOUNTLN_OVERHEAD = DUPCOUNTLN_OVERHEAD_32; if (isJVM14) { BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_32_14; DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_32_14; DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_32_14; IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_32_14; TXN_OVERHEAD = TXN_OVERHEAD_32_14; CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_32_14; } else { BIN_FIXED_OVERHEAD = BIN_FIXED_OVERHEAD_32_15; DIN_FIXED_OVERHEAD = DIN_FIXED_OVERHEAD_32_15; DBIN_FIXED_OVERHEAD = DBIN_FIXED_OVERHEAD_32_15; IN_FIXED_OVERHEAD = IN_FIXED_OVERHEAD_32_15; TXN_OVERHEAD = TXN_OVERHEAD_32_15; CHECKPOINT_REFERENCE_SIZE = CHECKPOINT_REFERENCE_SIZE_32_15; } KEY_OVERHEAD = KEY_OVERHEAD_32; LOCK_OVERHEAD = LOCK_OVERHEAD_32; LOCKINFO_OVERHEAD = LOCKINFO_OVERHEAD_32; UTILIZATION_PROFILE_ENTRY = UTILIZATION_PROFILE_ENTRY_32; TFS_LIST_INITIAL_OVERHEAD = TFS_LIST_INITIAL_OVERHEAD_32; TFS_LIST_SEGMENT_OVERHEAD = TFS_LIST_SEGMENT_OVERHEAD_32; LN_INFO_OVERHEAD = LN_INFO_OVERHEAD_32; LONG_LIST_PER_ITEM_OVERHEAD = LONG_LIST_PER_ITEM_OVERHEAD_32; } } /* public for unit tests. */ public final static long MIN_MAX_MEMORY_SIZE = 96 * 1024; public final static String MIN_MAX_MEMORY_SIZE_STRING = Long.toString(MIN_MAX_MEMORY_SIZE); private final static long N_64MB = (1 << 26); /* * Note that this class contains long fields that are accessed by multiple * threads, and access to these fields is intentionally not synchronized. * Although inaccuracies may result, correcting them is not worth the cost * of synchronizing every time we adjust the treeMemoryUsage or * miscMemoryUsage. */ /* * Amount of memory cached for tree objects. */ private long treeMemoryUsage; /* * Amount of memory cached for Txn and other objects. */ private long miscMemoryUsage; /* * Used to protect treeMemoryUsage and miscMemoryUsage updates. */ private Object memoryUsageSynchronizer = new Object(); /* * Number of lock tables (cache of EnvironmentParams.N_LOCK_TABLES). */ private int nLockTables; /* * Amount of memory cached for locks. Protected by the * LockManager.lockTableLatches[lockTableIndex]. Individual elements of * array may be negative, but the sum will be >= 0. */ private long[] lockMemoryUsage; /* * Memory available to JE, based on je.maxMemory and the memory available * to this process. */ private long maxMemory; private long criticalThreshold; // experimental mark for sync eviction. /* Memory available to log buffers. */ private long logBufferBudget; /* Maximum allowed use of the misc budget by the UtilizationTracker. */ private long trackerBudget; /* * Memory to hold internal nodes and misc memory (locks), controlled by the * evictor. Does not include the log buffers. */ private long cacheBudget; /* * Overheads that are a function of node capacity. */ private long inOverhead; private long binOverhead; private long dinOverhead; private long dbinOverhead; private EnvironmentImpl envImpl; MemoryBudget(EnvironmentImpl envImpl, DbConfigManager configManager) throws DatabaseException { this.envImpl = envImpl; /* Request notification of mutable property changes. */ envImpl.addConfigObserver(this); /* Peform first time budget initialization. */ reset(configManager); /* * Calculate IN and BIN overheads, which are a function of * capacity. These values are stored in this class so that they can be * calculated once per environment. The logic to do the calculations is * left in the respective node classes so it can be done properly in * the domain of those objects. */ inOverhead = IN.computeOverhead(configManager); binOverhead = BIN.computeOverhead(configManager); dinOverhead = DIN.computeOverhead(configManager); dbinOverhead = DBIN.computeOverhead(configManager); } /** * Respond to config updates. */ public void envConfigUpdate(DbConfigManager configManager) throws DatabaseException { /* * Reinitialize the cache budget and the log buffer pool, in that * order. Do not reset the log buffer pool if the log buffer budget * hasn't changed, since that is expensive and may cause I/O. */ long oldLogBufferBudget = logBufferBudget; reset(configManager); if (oldLogBufferBudget != logBufferBudget) { envImpl.getLogManager().resetPool(configManager); } } /** * Initialize at construction time and when the cache is resized. */ private void reset(DbConfigManager configManager) throws DatabaseException { /* * Calculate the total memory allotted to JE. * 1. If je.maxMemory is specified, use that. Check that it's * not more than the jvm memory. * 2. Otherwise, take je.maxMemoryPercent * JVM max memory. */ long newMaxMemory = configManager.getLong(EnvironmentParams.MAX_MEMORY); long jvmMemory = getRuntimeMaxMemory(); if (newMaxMemory != 0) { /* Application specified a cache size number, validate it. */ if (jvmMemory < newMaxMemory) { throw new IllegalArgumentException (EnvironmentParams.MAX_MEMORY.getName() + " has a value of " + newMaxMemory + " but the JVM is only configured for " + jvmMemory + ". Consider using je.maxMemoryPercent."); } if (newMaxMemory < MIN_MAX_MEMORY_SIZE) { throw new IllegalArgumentException (EnvironmentParams.MAX_MEMORY.getName() + " is " + newMaxMemory + " which is less than the minimum: " + MIN_MAX_MEMORY_SIZE); } } else { /* * When no explicit cache size is specified and the JVM memory size * is unknown, assume a default sized (64 MB) heap. This produces * a reasonable cache size when no heap size is known. */ if (jvmMemory == Long.MAX_VALUE) { jvmMemory = N_64MB; } /* Use the configured percentage of the JVM memory size. */ int maxMemoryPercent = configManager.getInt(EnvironmentParams.MAX_MEMORY_PERCENT); newMaxMemory = (maxMemoryPercent * jvmMemory) / 100; } /* * Calculate the memory budget for log buffering. If the LOG_MEM_SIZE * parameter is not set, start by using 7% (1/16th) of the cache * size. If it is set, use that explicit setting. * * No point in having more log buffers than the maximum size. If * this starting point results in overly large log buffers, * reduce the log buffer budget again. */ long newLogBufferBudget = configManager.getLong(EnvironmentParams.LOG_MEM_SIZE); if (newLogBufferBudget == 0) { newLogBufferBudget = newMaxMemory >> 4; } else if (newLogBufferBudget > newMaxMemory / 2) { newLogBufferBudget = newMaxMemory / 2; } /* * We have a first pass at the log buffer budget. See what * size log buffers result. Don't let them be too big, it would * be a waste. */ int numBuffers = configManager.getInt(EnvironmentParams.NUM_LOG_BUFFERS); long startingBufferSize = newLogBufferBudget / numBuffers; int logBufferSize = configManager.getInt(EnvironmentParams.LOG_BUFFER_MAX_SIZE); if (startingBufferSize > logBufferSize) { startingBufferSize = logBufferSize; newLogBufferBudget = numBuffers * startingBufferSize; } else if (startingBufferSize < EnvironmentParams.MIN_LOG_BUFFER_SIZE) { startingBufferSize = EnvironmentParams.MIN_LOG_BUFFER_SIZE; newLogBufferBudget = numBuffers * startingBufferSize; } long newCriticalThreshold = (newMaxMemory * envImpl.getConfigManager().getInt (EnvironmentParams.EVICTOR_CRITICAL_PERCENTAGE))/100; long newTrackerBudget = (newMaxMemory * envImpl.getConfigManager().getInt (EnvironmentParams.CLEANER_DETAIL_MAX_MEMORY_PERCENTAGE))/100; /* * If all has gone well, update the budget fields. Once the log buffer * budget is determined, the remainder of the memory is left for tree * nodes. */ maxMemory = newMaxMemory; criticalThreshold = newCriticalThreshold; logBufferBudget = newLogBufferBudget; trackerBudget = true?newTrackerBudget:newMaxMemory; cacheBudget = newMaxMemory - newLogBufferBudget; nLockTables = configManager.getInt(EnvironmentParams.N_LOCK_TABLES); lockMemoryUsage = new long[nLockTables]; } /** * Returns Runtime.maxMemory(), accounting for a MacOS bug. * May return Long.MAX_VALUE if there is no inherent limit. * Used by unit tests as well as by this class. */ public static long getRuntimeMaxMemory() { /* Runtime.maxMemory is unreliable on MacOS Java 1.4.2. */ if ("Mac OS X".equals(System.getProperty("os.name"))) { String jvmVersion = System.getProperty("java.version"); if (jvmVersion != null && jvmVersion.startsWith("1.4.2")) { return Long.MAX_VALUE; /* Undetermined heap size. */ } } return Runtime.getRuntime().maxMemory(); } /** * Initialize the starting environment memory state */ void initCacheMemoryUsage() throws DatabaseException { synchronized (memoryUsageSynchronizer) { treeMemoryUsage = calcTreeCacheUsage(); } assert LatchSupport.countLatchesHeld() == 0; } /** * Public for testing. */ public long calcTreeCacheUsage() throws DatabaseException { long totalSize = 0; INList inList = envImpl.getInMemoryINs(); inList.latchMajor(); try { Iterator iter = inList.iterator(); while (iter.hasNext()) { IN in = (IN) iter.next(); long size = in.getInMemorySize(); totalSize += size; } } finally { inList.releaseMajorLatch(); } return totalSize; } /** * Update the environment wide tree memory count, wake up the evictor if * necessary. * @param increment note that increment may be negative. */ public void updateTreeMemoryUsage(long increment) { synchronized (memoryUsageSynchronizer) { treeMemoryUsage += increment; } if (getCacheMemoryUsage() > cacheBudget) { envImpl.alertEvictor(); } } /** * Update the environment wide misc memory count, wake up the evictor if * necessary. * @param increment note that increment may be negative. */ public void updateMiscMemoryUsage(long increment) { synchronized (memoryUsageSynchronizer) { miscMemoryUsage += increment; } if (getCacheMemoryUsage() > cacheBudget) { envImpl.alertEvictor(); } } public void updateLockMemoryUsage(long increment, int lockTableIndex) { lockMemoryUsage[lockTableIndex] += increment; if (getCacheMemoryUsage() > cacheBudget) { envImpl.alertEvictor(); } } public long accumulateNewUsage(IN in, long newSize) { return in.getInMemorySize() + newSize; } public void refreshTreeMemoryUsage(long newSize) { synchronized (memoryUsageSynchronizer) { treeMemoryUsage = newSize; } } public long getCacheMemoryUsage() { long accLockMemoryUsage = 0; if (nLockTables == 1) { accLockMemoryUsage = lockMemoryUsage[0]; } else { for (int i = 0; i < nLockTables; i++) { accLockMemoryUsage += lockMemoryUsage[i]; } } return treeMemoryUsage + miscMemoryUsage + accLockMemoryUsage; } /** * Used for unit testing. */ public long getTreeMemoryUsage() { return treeMemoryUsage; } public long getLogBufferBudget() { return logBufferBudget; } public long getTrackerBudget() { return trackerBudget; } public long getMaxMemory() { return maxMemory; } public long getCriticalThreshold() { return criticalThreshold; } public long getCacheBudget() { return cacheBudget; } public long getINOverhead() { return inOverhead; } public long getBINOverhead() { return binOverhead; } public long getDINOverhead() { return dinOverhead; } public long getDBINOverhead() { return dbinOverhead; } /** * Returns the memory size occupied by a byte array of a given length. */ public static int byteArraySize(int arrayLen) { /* * BYTE_ARRAY_OVERHEAD accounts for 4 bytes of data. Data larger than * 4 bytes is allocated in 8 byte increments. */ int size = BYTE_ARRAY_OVERHEAD; if (arrayLen > 4) { size += ((arrayLen - 4 + 7) / 8) * 8; } return size; } void loadStats(StatsConfig config, EnvironmentStats stats) { stats.setCacheDataBytes(getCacheMemoryUsage()); } }